package com.mf.basic.debounces;

import cn.dev33.satoken.stp.StpUtil;
import com.mf.basic.enums.EnumBaseParams;
import com.mf.basic.exceptions.MyException;
import com.mf.basic.utils.UtilsRedis;
import jakarta.annotation.Resource;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.DigestUtils;

import java.util.Arrays;
import java.util.concurrent.TimeUnit;

/**
 * Created by Luke on 2025/3/9 16:34.
 */
@Aspect
@Component
@Order(1)
public class DebounceAspect {

    @Resource
    private UtilsRedis redis;

    @Around("@annotation(debounce)")
    public Object debounce(ProceedingJoinPoint joinPoint, Debounce debounce) throws Throwable {
        // 生成唯一标识Key(用户ID + 方法签名 + 参数哈希),确保不同用户、不同方法、不同参数的请求不会互相干扰
        String key = generateKey(joinPoint);
        // 检查Redis中是否存在该Key, 利用setIfAbsent的原子性操作，确保防抖间隔
        Boolean isAbsent = redis.getRedisTemplate()
                .opsForValue()
                .setIfAbsent(key, "1", debounce.interval(), TimeUnit.MILLISECONDS);
        if (Boolean.FALSE.equals(isAbsent)) {
            throw new MyException("请勿重复提交");
        }
        // 执行业务方法
        return joinPoint.proceed();
    }

    /**
     * 生成唯一标识Key（用户ID + 方法签名 + 参数哈希）
     * @param joinPoint ProceedingJoinPoint
     * @return String
     */
    private String generateKey(ProceedingJoinPoint joinPoint) {
        StringBuilder sb = new StringBuilder();
        // 添加用户ID（根据实际项目获取用户信息，例如从Token中解析）
        try{
            long userId = (long) StpUtil.getSession().get(EnumBaseParams.userId.name());
            sb.append(userId).append(":");
        } catch (Exception e) {
            sb.append("0:");
        }
        // 添加方法签名
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        sb.append(signature.getMethod().getDeclaringClass().getName())
                .append("#")
                .append(signature.getMethod().getName());
        // 添加参数哈希（防止不同参数重复）
        Object[] args = joinPoint.getArgs();
        if (args.length > 0) {
            String params = Arrays.deepToString(args);
            String paramsHash = DigestUtils.md5DigestAsHex(params.getBytes());
            sb.append(":").append(paramsHash);
        }
        return "debounce:" + sb;
    }
}
